home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1987 / 07 / lindlst.jul < prev    next >
Text File  |  1987-07-28  |  22KB  |  918 lines

  1.  
  2. {$K-}             {Compiler switch - never change}
  3.  
  4. {************************************************}
  5. {***                 Listing One              ***}
  6. {***                Turbo Pascal              ***}
  7. {***            Multitasking Kernel           ***}
  8. {***                 written by               ***}
  9. {***              Craig A. Lindley            ***}
  10. {***                                          ***}
  11. {***    Ver: 1.3     Last update: 03/11/87    ***}
  12. {***                                          ***}
  13. {************************************************}
  14.  
  15. CONST
  16.  
  17.    task_stack_size  = 256; {stack size for each}
  18.                            {task}
  19.    turbodseg: integer = 0; {storage for turbos}
  20.                            {data segment value}
  21.  
  22.  
  23. TYPE
  24.  
  25. {possible states for a task}
  26.    task_state = (ready,waiting,running);
  27.  
  28. {808X register set}
  29.    register_type = RECORD
  30.    CASE integer OF
  31.       1: (ax,bx,cx,dx,bp,si,di,ds,es,flags:integer);
  32.       2: (al,ah,bl,bh,cl,ch,dl,dh         :byte);
  33.    END;
  34.  
  35.  
  36. {Task control block (tcb) structure}
  37.  
  38.  tcbptr = ^ tcb;       {ptr to tcb}
  39.  
  40.  tcb = RECORD
  41.     link:  tcbptr;     {link to next tcb in dseg}
  42.     bptr:  integer;    {base ptr offset in sseg}
  43.     state: task_state; {ready, waiting, running}
  44.     id:    byte;       {task number}
  45.  END;
  46.  
  47.  waitptr = ^tcbptr;    {ptr to ptr to tcb}
  48.                        {used for passing parms}
  49.                        {to wait}
  50.  
  51. {This fifo overhead structure is the same for}
  52. {all fifo types regardless of the items to be}
  53. {stored in the fifo. The byte fifo is an example}
  54. {of just one possible type of fifo.}
  55.  
  56.  overhead = RECORD     {fifo overhead data}
  57.                        {structure}
  58.     count,             {# of items in fifo}
  59.     inptr,             {ptr to where items are}
  60.                        {stored}
  61.     outptr:   integer; {ptr to where items are}
  62.                        {fetched}
  63.     not_empty,         {ptrs to waiting tasks}
  64.     not_full: tcbptr;
  65.  END;
  66.  
  67.  
  68.  bytefifo = RECORD     {definition of a byte fifo}
  69.     ovd:  overhead;    {fifo overhead}
  70.     data: ARRAY[1..bytefifosize]
  71.            OF byte;    {byte fifo data area}
  72.  END;
  73.  
  74.  
  75.  semaphore = RECORD    {Semaphore data type}
  76.     count:  integer;   {number of times signaled}
  77.     signal: tcbptr;    {pointer to waiting task}
  78.                        {if there is one}
  79.  END;
  80.  
  81.  
  82. {******** Begin Multitasking Variables *********}
  83.  
  84. VAR
  85.  
  86.    cp,                  {current task pointer}
  87.    new_tcb_ptr,         {ptr to new tcb in dseg}
  88.    temp_ptr:   tcbptr;
  89.  
  90.    waitfor:    waitptr; {address of item to}
  91.                         {wait on}
  92.    stk,bp:     integer; {variables for setting}
  93.                         {808X sp and bp}
  94.    frame_ptr:  integer; {stack frame pointer}
  95.    next_id:    integer; {next task id number}
  96.    i:          integer;
  97.    child_process: boolean; {fork successful flag}
  98.  
  99.  
  100. {******** Begin Multitasking Procedures ********}
  101.  
  102.  
  103. PROCEDURE Fork;        {fork off a new task}
  104.  
  105. {This procedure manipulates Turbo Pascal's stack}
  106. {frame as required to fool it into operating in}
  107. {a new task's environment.}
  108.  
  109. BEGIN
  110.  
  111.    child_process:=false;   {indicate the parent}
  112.                            {process until proven}
  113.                            {otherwise}
  114.    {check if enough stack space for a new task}
  115.  
  116.    IF abs(frame_ptr - task_stack_size) > 0 THEN
  117.    BEGIN                   {if enough}
  118.       INLINE($89/$26/stk); {get 808X Sp to}
  119.                            {calculate Bp pointer}
  120.       cp^.bptr:=stk+2;     {save Bp ptr in this}
  121.                            {frame}
  122.       new(new_tcb_ptr);    {allociate new tcb}
  123.  
  124.       {link new tcb into scheduler loop}
  125.       {make its state running and give it an id}
  126.  
  127.       new_tcb_ptr^.link:=cp^.link;
  128.       cp^.link:=new_tcb_ptr;
  129.       new_tcb_ptr^.state:=running;
  130.       next_id:=next_id+1;
  131.       new_tcb_ptr^.id:=next_id;
  132.  
  133.       cp^.state:=ready;    {old frame is ready}
  134.  
  135.       {copy old stack to new stack frame}
  136.       FOR i:=0 TO 5 DO
  137.          mem[sseg:frame_ptr-6+i]:=mem[sseg:stk+i];
  138.  
  139.       {make Bp storage in stack frame point at}
  140.       {this frame}
  141.  
  142.       memw[sseg:frame_ptr-4]:=frame_ptr;
  143.       bp:=frame_ptr-4;     {calculate Bp pointer}
  144.  
  145.       INLINE($8B/$2E/bp);  {set 808X Bp reg to}
  146.                            {this new value}
  147.  
  148.      {reserve stack frame space}
  149.      frame_ptr:=frame_ptr-task_stack_size;
  150.      cp:=new_tcb_ptr;      {cp points at new task}
  151.      child_process:=true;  {indicate child process}
  152.    END;
  153.  
  154. END;
  155.  
  156.  
  157.  
  158. PROCEDURE Yield;
  159.  
  160. {This procedure cause the executing task to}
  161. {relinquish control of the CPU to the next ready}
  162. {task.}
  163.  
  164. BEGIN
  165.  
  166.    child_process:=false;   {reset variable}
  167.    IF cp^.link <> cp THEN  {must have more than}
  168.                            {one task forked to be}
  169.                            {able to yield}
  170.    BEGIN
  171.       INLINE($89/$26/stk); {get 808X sp}
  172.       cp^.bptr:=stk+2;     {save Bp ptr in}
  173.                            {current task frame}
  174.       cp^.state:=ready;    {yielded task ready}
  175.       temp_ptr:=cp;
  176.  
  177.       {look for next ready task in scheduler loop}
  178.       {there must be at least one or else}
  179.  
  180.       WHILE (temp_ptr^.link^.state <> ready) DO
  181.          temp_ptr:=temp_ptr^.link;
  182.  
  183.       cp:=temp_ptr^.link;  {cp points at new task}
  184.       cp^.state:=running;  {indicate running}
  185.       bp:=cp^.bptr;        {get the bp of task}
  186.  
  187.      INLINE($8B/$2E/bp);   {restore it to 808X bp}
  188.    END
  189.    ELSE
  190.    BEGIN
  191.       writeln('Cannot yield only single task running');
  192.       halt;
  193.    END;
  194.  
  195. END;
  196.  
  197.  
  198.  
  199. PROCEDURE Wait;  {put current task in wait mode}
  200.                  {until a send makes it ready}
  201.  
  202. {Due to constraints of this kernel, parameters}
  203. {cannot be passed directly to the wait procedure.}
  204. {To overcome this limitation, a global variable}
  205. {called waitfor is used. The address of the}
  206. {tcbptr on which to wait should be stored in}
  207. {waitfor. See the fifo routines for an example of}
  208. {the proper usage of Wait.}
  209.  
  210. BEGIN
  211.  
  212.    child_process:=false;   {reset variable}
  213.    IF cp^.link <> cp THEN  {must have more than}
  214.                            {one task forked to be}
  215.                            {able to wait}
  216.    BEGIN
  217.       waitfor^ := cp;      {waitfor points at the}
  218.                            {current task}
  219.  
  220.       INLINE($89/$26/stk); {get 808X sp}
  221.       cp^.bptr:=stk+2;     {save it in current}
  222.                            {task frame}
  223.       cp^.state:=waiting;  {task now waiting}
  224.       temp_ptr:=cp;
  225.  
  226.       {look for next ready task in scheduler loop}
  227.       {there must be at least one or else}
  228.  
  229.       WHILE (temp_ptr^.link^.state <> ready) DO
  230.          temp_ptr:=temp_ptr^.link;
  231.  
  232.       cp:=temp_ptr^.link;  {cp points at new task}
  233.       cp^.state:=running;  {indicate running}
  234.       bp:=cp^.bptr;        {get bp for this task}
  235.       INLINE($8B/$2E/bp);  {restore it to 808X bp}
  236.    END
  237.    ELSE
  238.    BEGIN
  239.       writeln('Cannot wait only single task running');
  240.       halt;
  241.    END;
  242.  
  243. END;
  244.  
  245.  
  246. PROCEDURE Send(VAR s:tcbptr);
  247.  
  248. {Make the specified task ready for next scheduler}
  249. {go around}
  250.  
  251. BEGIN
  252.  
  253.    s^.state:=ready;    {task state is ready}
  254.    s:=NIL;             {clear pointer}
  255.  
  256. END;
  257.  
  258.  
  259.  
  260. PROCEDURE Pause(t:integer);
  261.  
  262. {Pause the execution of a task for t 1/4 sec}
  263. {intervals. Note even t results in more}
  264. {accurate timmings.}
  265.  
  266.  FUNCTION tic_count : integer;
  267.  
  268.  {Get the current tic count from the Bios}
  269.  
  270.  VAR
  271.  
  272.     regs: register_type;
  273.  
  274.  BEGIN
  275.  
  276.     regs.ax:=0;         {request clock tic read}
  277.     intr($1A,regs);
  278.     tic_count:=regs.dx; {LSB of count in dx}
  279.  
  280.  END;
  281.  
  282.  
  283. VAR
  284.  
  285.    tics,i: integer;
  286.  
  287. BEGIN
  288.  
  289.    tics:=0;             {initial tic count to 0}
  290.    IF t > 0 THEN        {if a legal tic count}
  291.    BEGIN
  292.       FOR i:=1 TO t DO  {250 msec = 4.55 tics}
  293.          IF odd(i) THEN {use this algorithm for}
  294.                         {approximation}
  295.             tics:=tics+4 {250 msec = 4.5 tics}
  296.          ELSE
  297.             tics:=tics+5;
  298.  
  299.       {add tics to current tic_count to get}
  300.       tics:=tics+tic_count;    {target time}
  301.  
  302.       REPEAT
  303.          yield;    {return when tic count is}
  304.                    {reached or exceeded}
  305.       UNTIL tics <= tic_count;
  306.    END
  307.    ELSE
  308.       writeln('Bad tic count specified');
  309.  
  310. END;
  311.  
  312.  
  313. PROCEDURE Init_Kernel;
  314.  
  315. {This procedure initializes the multitasking}
  316. {for use. It sets up the TCB for task 0 and}
  317. {indicates that it is running.}
  318.  
  319. Begin
  320.  
  321.    turbodseg:=dseg;     {save turbo data segment}
  322.    frame_ptr:= $FFFE;   {initial stack location}
  323.    next_id:=0;          {first task id}
  324.    new(new_tcb_ptr);    {get new tcb in dseg}
  325.    cp:=new_tcb_ptr;     {cp points at tcb}
  326.    cp^.link:=cp;        {points at itself}
  327.    cp^.state:=running;  {in running state}
  328.    cp^.id:=next_id;     {id = 0}
  329.  
  330.    {now allociate 1st frame for task 0}
  331.    frame_ptr:=frame_ptr-task_stack_size;
  332.  
  333. End;
  334.  
  335.  
  336. {************ Begin FIFO Procedures ************}
  337.  
  338.  
  339. PROCEDURE Initialize_fifo(VAR o:overhead);
  340.  
  341. {Initialize a fifo's overhead data structure.}
  342. {This procedure will work with any type fifo.}
  343. {This makes the fifo appear empty.}
  344.  
  345. BEGIN
  346.  
  347.    o.count:= 0;        {count is empty}
  348.    o.inptr:=1;         {ptrs to 1st entry}
  349.    o.outptr:=1;        {put in and take out at}
  350.                        {entry 1}
  351.    o.not_empty:=NIL;   {signals to nil}
  352.    o.not_full:=NIL;
  353.  
  354. END;
  355.  
  356.  
  357. PROCEDURE Put_byte(b:byte; VAR f:bytefifo);
  358.  
  359. {This procedure manages the input of data into}
  360. {a byte fifo. If the fifo is full when this}
  361. {procedure is called, the task that called it}
  362. {will be put to sleep automatically until there}
  363. {is room in the fifo for the data byte.}
  364. {The fifo overhead data structure is modified}
  365. {whenever a byte is placed into the fifo}
  366.  
  367. BEGIN
  368.  
  369.    WITH f.ovd DO
  370.    BEGIN                {check if fifo full}
  371.       IF count = bytefifosize THEN
  372.       BEGIN             {if so go to sleep}
  373.          waitfor := addr (not_full);
  374.          wait;
  375.       END;              {when not full add}
  376.       count:=count+1;   {one more to count}
  377.       f.data[inptr]:=b; {store the byte}
  378.       inptr:=inptr+1;   {bump input pointer}
  379.       IF inptr > bytefifosize THEN
  380.          inptr:=1;      {wrap ptr if necessary}
  381.  
  382.       {if waiters for this fifo wake them}
  383.  
  384.       IF not_empty <> NIL THEN
  385.          send(not_empty);
  386.    END;
  387.  
  388. END;
  389.  
  390.  
  391. FUNCTION Get_byte(VAR f:bytefifo) : byte;
  392.  
  393. {This procedure manages the output of data from}
  394. {a byte fifo. If the fifo is empty when this}
  395. {procedure is called, the task that called it}
  396. {will be put to sleep automatically until there}
  397. {is data in the fifo to retrieve.}
  398. {The fifo overhead data structure is modified}
  399. {whenever a byte is removed from the fifo}
  400.  
  401. BEGIN
  402.  
  403.    WITH f.ovd DO
  404.    BEGIN               {check if fifo empty}
  405.       IF count = 0 THEN
  406.       BEGIN            {if so go to sleep}
  407.          waitfor := addr (not_empty);
  408.          wait;
  409.       END;
  410.                        {when data is available}
  411.       count:=count-1;  {one less to count}
  412.       get_byte:=f.data[outptr];  {get the byte}
  413.       outptr:=outptr+1;{bump output pointer}
  414.       IF outptr > bytefifosize THEN
  415.          outptr:=1;    {wrap ptr if necessary}
  416.  
  417.       {if waiters for this fifo wake them}
  418.  
  419.       IF not_full <> NIL THEN
  420.          send(not_full);
  421.    END;
  422.  
  423. END;
  424.  
  425.  
  426. {********* Begin Semaphore Procedures **********}
  427.  
  428. PROCEDURE Initialize_semaphore(VAR s:semaphore);
  429.  
  430. {Initialize a semaphore data structure}
  431.  
  432. BEGIN
  433.  
  434.    s.count := 0;       {indicate resource is}
  435.                        {available}
  436.    s.signal:=NIL;      {and that there are no}
  437.                        {waiters}
  438.  
  439. END;
  440.  
  441.  
  442.  
  443. PROCEDURE Alloc(VAR s:semaphore);
  444.  
  445. {This procedure allociates exclusive use of a}
  446. {resource to the task that executes it. This}
  447. {claim is maintained even though the task}
  448. {gives up control of the CPU via a yield etc.}
  449.  
  450. BEGIN
  451.  
  452.    WHILE s.count <> 0 DO  {wait for semaphore}
  453.                           {controlled resource}
  454.                           {to become available}
  455.    BEGIN
  456.       waitfor := addr (s.signal);
  457.       wait;
  458.    END;                   {then}
  459.    s.count:=1;            {claim it}
  460.  
  461. END;
  462.  
  463.  
  464. PROCEDURE Dealloc(VAR s:semaphore);
  465.  
  466. {This procedure deallociates a resource.}
  467. {Note this routine yields so the deallociated}
  468. {resource has a chance of being used}
  469. {immediately}
  470.  
  471. BEGIN
  472.  
  473.    s.count:=0;     {remove claim on resource}
  474.    send(s.signal); {and awaken the waiting task}
  475.    yield;          {give other tasks a chance}
  476.  
  477. END;
  478.  
  479. {End of kernel procedures}
  480.  
  481.  
  482.  
  483.  
  484. PROGRAM Multitasking_Demonstration_Program;
  485.  
  486. {************************************************}
  487. {***                Listing Two               ***}
  488. {***        Multitasking Demonstration        ***}
  489. {***         A dumb terminal program          ***}
  490. {*** utilizing 4 tasks and a serial interrupt ***}
  491. {***              service routine.            ***}
  492. {***                                          ***}
  493. {***                 written by               ***}
  494. {***              Craig A. Lindley            ***}
  495. {***                                          ***}
  496. {***    Ver: 1.0     Last update: 03/11/87    ***}
  497. {***                                          ***}
  498. {************************************************}
  499.  
  500. CONST
  501.  
  502.  bytefifosize = 100;    {max size of byte fifos}
  503.  
  504.  
  505. {include the multitasking kernel routines}
  506. {$I multi.pas}
  507.  
  508. {include the RS-232 functions}
  509. {$I serial.pas}
  510.  
  511.  
  512. VAR
  513.  
  514.    inbuffer,
  515.    outbuffer:     bytefifo;
  516.  
  517.  
  518. {***** Serial Interface Support Procedures *****}
  519.  
  520. PROCEDURE Get_serial_char;
  521.  
  522. VAR
  523.  
  524.    b:   byte;
  525.  
  526. BEGIN
  527.  
  528. {Get the character from the UART. Place it in}
  529. {inbuffer if there is room, throw it away if}
  530. {not. Signal end of interrupt (EOI level 4 on}
  531. {8259.}
  532.  
  533.    b := port[portaddress];
  534.  
  535.    IF inbuffer.ovd.count < bytefifosize THEN
  536.       Put_byte(b,inbuffer);
  537.  
  538.    port[$20] := $20;
  539.  
  540. END;
  541.  
  542.  
  543.  
  544. PROCEDURE Serial_Interrupt_Service_Routine;
  545.  
  546. {This is the new interrupt service routine.}
  547. {It replaces the one MsDos normally uses.}
  548. {See text for details.}
  549.  
  550. BEGIN
  551.  
  552. {standard interrupt service routine preamble}
  553.  
  554.  INLINE($50/$53/$51/$52/$57/   {Push ax,bx,cx,dx,}
  555.         $56/$06/$1e/           {di,si,es,ds}
  556.         $2e/$a1/turbodseg/     {mov ax,cs:turbodseg}
  557.         $8e/$d8/               {mov ds,ax}
  558.         $fb);                  {sti}
  559.  
  560.  Get_serial_char;
  561.  
  562.  {standard interrupt service routine postamble}
  563.  
  564.  INLINE($fa/$1f/$07/$5e/$5f/   {interrupts off}
  565.         $5a/$59/$5b/$58/       {Pop ds,es,si,di,}
  566.                                {dx,cx,bx,ax}
  567.         $5d/$5d/$cf);          {trash sp, restore}
  568.                                {Bp and iret}
  569.  
  570. END;
  571.  
  572.  
  573. {************ Begin Task Procedures ************}
  574.  
  575.  
  576. PROCEDURE Task_0;
  577.  
  578. {Task 0 gets keyboard input and puts it into}
  579. {outbuffer. If no input available task 0 yields.}
  580. {Note infinite loop structure.}
  581.  
  582. VAR
  583.  
  584.    ch:    char;
  585.  
  586. BEGIN
  587.  
  588.    writeln('Starting Task 0');
  589.    writeln;
  590.    REPEAT
  591.       IF NOT keypressed THEN
  592.          Yield
  593.       ELSE
  594.       BEGIN
  595.          read(kbd,ch);
  596.          Put_byte(byte(ch),outbuffer);
  597.       END;
  598.    UNTIL false;
  599.  
  600. END;
  601.  
  602.  
  603. PROCEDURE Task_1;
  604.  
  605. {Task 1 takes character from outbuffer using}
  606. {Get_Byte and sends them out the serial port.}
  607.  
  608. BEGIN
  609.  
  610.    writeln('Starting Task 1');
  611.    REPEAT
  612.       Serialout(Get_byte(outbuffer));
  613.    UNTIL false;
  614.  
  615. END;
  616.  
  617.  
  618. PROCEDURE Task_2;
  619.  
  620. {Task 2 retrives characters placed in inbuffer}
  621. {by the serial interrupt routine and displays}
  622. {them on the screen. Note:}
  623. { 1) If no characters are available this routine}
  624. {    yields.
  625. { 2) Interrupts must be disabled while inbuffer}
  626. {    is being accessed. Otherwise the fifo}
  627. {    counter will get confused and this program}
  628. {    will eventually crash.}
  629.  
  630.  
  631. BEGIN
  632.  
  633.    writeln('Starting Task 2');
  634.    REPEAT
  635.  
  636.       IF inbuffer.ovd.count <> 0 THEN
  637.       BEGIN
  638.          INLINE($FA);   {interrupts off}
  639.          write(chr(Get_byte(inbuffer)));
  640.          INLINE($FB);   {interrupts on }
  641.       END
  642.       ELSE
  643.          Yield;
  644.  
  645.    UNTIL false;
  646.  
  647. END;
  648.  
  649.  
  650. PROCEDURE Task_3;
  651.  
  652. {Task 3 monitors and displays the fifo and cursor}
  653. {status. It wakes up every 1/2 second to do so.}
  654. {The cursor position is saved and retrived while}
  655. {the fifo status is being updated on the screen}
  656.  
  657. VAR
  658.  
  659.    cursorx,
  660.    cursory: byte;
  661.  
  662. BEGIN
  663.  
  664.    writeln('Starting Task 3');
  665.    REPEAT
  666.       pause(2);          {wake every 1/2 sec}
  667.       cursorx := wherex; {save cursor position}
  668.       cursory := wherey;
  669.       window(1,1,80,25);
  670.       gotoxy(21,25);
  671.       write(cursorx:2);  {write cursor position}
  672.       gotoxy(35,25);
  673.       write(cursory:2);
  674.       gotoxy(58,25);     {write fifo counts}
  675.       write(inbuffer.ovd.count:2);
  676.       gotoxy(72,25);
  677.       write(outbuffer.ovd.count:2);
  678.       window(1,1,80,23);
  679.       gotoxy(cursorx,cursory);
  680.    UNTIL false;
  681.  
  682. END;
  683.  
  684.  
  685. PROCEDURE Initialize_Display;
  686.  
  687. {This procedure initializes the screen for the}
  688. {demo program. It builds a status line on screen}
  689. {line 25 and then establishes a terminal window}
  690. {so the status line will not be over written.}
  691.  
  692. BEGIN
  693.  
  694.    window(1,1,80,25);   {window is full screen}
  695.    CLRSCR;              {clear the screen}
  696.  
  697.    writeln('Multitasking Demonstration');
  698.    writeln('  A dumb serial terminal program');
  699.    writeln('  Use ^C to abort');
  700.    writeln;
  701.  
  702.    {build the status line}
  703.    gotoxy( 1,25);  write('Status -- Cursor X:');
  704.    gotoxy(25,25);  write('Cursor Y:');
  705.    gotoxy(49,25);  write('Incount:');
  706.    gotoxy(62,25);  write('Outcount:');
  707.    window(1,1,80,23);   {establish terminal window}
  708.    gotoxy(1,8);         {home the cursor}
  709.  
  710. END;
  711.  
  712.  
  713. {************* Begin Main Program **************}
  714.  
  715. BEGIN {main}
  716.  
  717. {The main program builds the screen status}
  718. {line, initializes the input and output fifos,}
  719. {initializes the multitasking kernel, installs}
  720. {the serial interrupt handler and then begins}
  721. {forking the individual tasks.}
  722.  
  723.    Initialize_Display;
  724.    Initialize_fifo(inbuffer.ovd);
  725.    Initialize_fifo(outbuffer.ovd);
  726.    Init_Kernel;
  727.  
  728. {Initialize and install the serial interrupt}
  729. {handler. Install our interrupt routine in}
  730. {place of the original system IRQ4 handler}
  731.  
  732.    WITH regs DO
  733.    BEGIN
  734.       ah:=$25;
  735.       al:=$0C;
  736.       ds:=cseg;
  737.       dx:=ofs(Serial_Interrupt_Service_Routine);
  738.       msdos(regs);
  739.    END;
  740.  
  741. {Set the serial format to 1200 baud}
  742. {1 stop bit, 8 data bits and no parity}
  743.  
  744.  Setserial(1200,1,8,none);
  745.  
  746. {Fork off tasks one through three}
  747.  
  748.  Fork;
  749.  
  750.  IF child_process THEN
  751.   Task_1;
  752.  
  753.  Fork;
  754.  
  755.  IF child_process THEN
  756.    Task_2;
  757.  
  758.  Fork;
  759.  
  760.  IF child_process THEN
  761.    Task_3;
  762.  
  763. {Enable the serial interrupt}
  764.  
  765.  Enable_serial_int;
  766.  
  767. {Start Task 0}
  768.  
  769.  Task_0;
  770.  
  771. END.
  772.  
  773.  
  774.  
  775.  
  776.  
  777. {************************************************}
  778. {***               Listing Three              ***}
  779. {***        Multitasking Demonstration        ***}
  780. {***             support routines             ***}
  781. {***                                          ***}
  782. {***                 written by               ***}
  783. {***              Craig A. Lindley            ***}
  784. {***                                          ***}
  785. {***    Ver: 1.0     Last update: 03/11/87    ***}
  786. {***                                          ***}
  787. {************************************************}
  788.  
  789. CONST
  790.  
  791.    COM1        = 1;      {com one PC port}
  792.    portaddress = $3f8;   {address of UART for}
  793.                          {COM1}
  794.  
  795. TYPE
  796.  
  797.    parity_type = (odd,even,none);
  798.  
  799. VAR
  800.  
  801.    regs:   register_type;
  802.  
  803.  
  804. PROCEDURE Int14(portnumber,command,
  805.                 parameter:integer);
  806.  
  807. {Procedure to initialize the com ports}
  808.  
  809. BEGIN
  810.  
  811.    WITH regs DO
  812.    BEGIN
  813.       dx := portnumber - 1;
  814.       ah := command;
  815.       al := parameter;
  816.       flags := 0;
  817.       intr($14,regs);
  818.    END;
  819.  
  820. END;
  821.  
  822.  
  823. PROCEDURE Setserial(baudrate,stopbits,
  824.                     databits: integer;
  825.                     parity: parity_type);
  826.  
  827. {Configure COM1 with the specified parameters}
  828.  
  829. VAR
  830.  
  831.     parameter: integer;
  832.  
  833. BEGIN
  834.  
  835.    writeln('Configuring the serial parameters');
  836.    writeln;
  837.    CASE baudrate OF
  838.       300: baudrate := 2;
  839.      1200: baudrate := 4;
  840.       ELSE baudrate := 4;  {default is 1200 baud}
  841.    END;
  842.  
  843.    IF stopbits = 2 THEN
  844.       stopbits := 1
  845.    ELSE
  846.       stopbits := 0;       {default is 1 stop bit}
  847.  
  848.    IF databits = 7 THEN
  849.       databits := 2
  850.    ELSE
  851.       databits := 3;       {default is 8 bit words}
  852.  
  853.    parameter := (baudrate SHL 5)+
  854.                 (stopbits SHL 2)+databits;
  855.  
  856.    CASE parity OF
  857.       odd: parameter := parameter + 8;
  858.      even: parameter := parameter + 24;
  859.      none: ;
  860.    END;
  861.  
  862.    Int14(COM1,0,parameter);{do the configuration}
  863.  
  864. END;
  865.  
  866.  
  867. FUNCTION Serialstatus : integer;
  868.  
  869. {Get the status of COM1 port}
  870.  
  871. BEGIN
  872.  
  873.    Int14(COM1,3,0);
  874.    serialstatus := regs.ax;
  875.  
  876. END;
  877.  
  878.  
  879. PROCEDURE Serialout(b:byte);
  880.  
  881. {Send a byte out the COM1 port}
  882.  
  883. BEGIN
  884.  
  885.    {wait till UART is ready}
  886.    WHILE (Serialstatus AND $2000) = 0 DO;
  887.  
  888.    {then send the byte out}
  889.    port[portaddress] := b;
  890.  
  891. END;
  892.  
  893.  
  894.  
  895. PROCEDURE Enable_serial_int;
  896.  
  897. BEGIN
  898.  
  899. {clear the serial interface of any garbage}
  900.  
  901.  INLINE($BA/portaddress  /$EC/
  902.         $BA/portaddress+5/$EC/
  903.         $BA/portaddress+6/$EC);
  904.  
  905.  INLINE($E4/$21/$24/$EF/$E6/$21); {IRQ4 enabled}
  906.  port[portaddress+4] := $0B;      {set DTR, RTS}
  907.                                   {and OUT2}
  908.  port[portaddress+1] := 1;        {receiver}
  909.                                   {interrupt}
  910.                                   {enabled}
  911. END;
  912.  
  913. {End of RS-232 procedures}
  914.  
  915. 
  916.